package ar.unc.famaf.pln.giveselector;

import give.nlgserver.NlgSystem;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;

import ar.unc.famaf.pln.giveselector.loganalyser.LogAnalyser;


/**
 * Allows to send string messages ensuring that are shown during 
 * the given time in ms.
 * 
 * @author David Racca ( david.racca@gmail.com )
 *
 */
public class SendingThread extends Thread 
{
	private NlgSystem system;
	private BlockingQueue<Message> toSendQueue;
	private boolean finished;
	private AtomicBoolean sending;
	private int time_factor;
	private int min_time;
	private final static int DEFAULT_SEND_TIME = 5000;


	/**
	 * Creates a SendingThread class.
	 * @param system
	 */
	public SendingThread(NlgSystem system, int time_factor, int min_time) 
	{
		this.system = system;
		this.time_factor = time_factor;
		this.min_time = min_time;
		this.toSendQueue = new LinkedBlockingQueue<Message>();
		this.finished = false;
		this.sending = new AtomicBoolean();
	}


	/**
	 * Represents an instruction message and contains
	 * the time in ms that the message should be shown
	 * to the DF.
	 */
	private class Message 
	{
		public String text;
		public int time;
		
		public Message(String text, int time)
		{
			this.text = text;
			this.time = time;
		}
	}


	/**
	 * Enqueues the given text for send it
	 */
	public void enqueue(String text, boolean showShortTime) 
	{
		int t = 0;
		if (showShortTime)
			t = 3000;
		else
			t = getTime(text);

		toSendQueue.add(new Message(text, t));
	}


	/**
	 * @return The time in milliseconds that the given
	 * message should be shown to the DF.
	 */
	protected int getTime(String text)
	{
		String cleanned = LogAnalyser.cleanInstruction(text);
		return Math.max(cleanned.length() * time_factor, min_time);
	}

	
	/**
	 * Shut down the sending thread.
	 */
	public void shutdown() 
	{
		finished = true;
		if (!sending.get())
			interrupt();
	}


	/**
	 * Clear the message queue. This will remove any unsent messages from the
	 * queue and ensure that the next message you enqueue will be sent
	 * immediately.
	 */
	public void clear()
	{
		toSendQueue.clear();
		if (!sending.get())
			interrupt();
	}


	@Override
	public void run() 
	{
		while (!finished) 
		{
			try 
			{
				Message next = toSendQueue.take();
				int time = next.time;

	            int mod = time % DEFAULT_SEND_TIME;
	            if (mod > 0)
	            {
	            	sending.set(true);
	            	system.send(next.text);
	            	sending.set(false);
	            	sleep(mod);
	            	time -= mod;
	            }
	            while (time > 0)
	            {
	            	sending.set(true);
	            	system.send(next.text);
	            	sending.set(false);
	            	sleep(DEFAULT_SEND_TIME);
	            	time -= DEFAULT_SEND_TIME;
	            }
			}
			catch (InterruptedException e) 
			{
				if (finished)
					break;
	        }
	    }
		System.out.println("SendingThread Ended");
	}


	/**
	 * @return True is this thread has finished.
	 */
	public boolean isFinished() 
	{
		return finished;
	}

}
